//onemax problem-depended class
//$Id: onemax.h 441 2010-04-09 17:27:20Z Oleg.Bulychov $
#pragma once

#include "src/heo/include/random.h"
#include "src/heo/include/array.h"
#include "src/heo/include/fast_stream.h"
#include "src/heo/include/reflection.h"
#include "src/heo/include/stream_serializer.h"
#include "src/heo/include/optimization_direction.h"
#include "solvers/ga/ga_common.h"
//__________________________________________________________________________________
struct OneMax_Config
{
    double initial_probability_;
    int dimension_;

    explicit OneMax_Config():
        initial_probability_(),
        dimension_()
    {}

    static char const* get_Name() { return "onemax"; }

    template<class V>
    void accept(V& v)
    {
        v(VAR_(initial_probability),        0.5);
        v(VAR_(dimension),                  100);
    }
};
//__________________________________________________________________________________
struct OneMax_Problem
{
    static char const* get_Default_File_Name() { return ""; }

    template<class I, class E>
    bool read(I& in, E* env, uniform_random& rnd)
    {
        return false;
    }
};
//__________________________________________________________________________________
class OneMax_Solution
{
    //DISALLOW_COPY_AND_ASSIGN(OneMax_Solution);
public:
    typedef maximization<> TDirection;
    typedef int            TCostType;
protected:
    Array<bool>  data_;
    TCostType    cost_;
    bool         changed_;
public:
    explicit OneMax_Solution(): changed_()
    {}

    template<class E>
    void init(E* env, uniform_random& rnd)
    {
        size_t dim = env->cfg_.dimension_;
        data_.set_Size(dim);
    }

    template<class E>
    void generate(E* env, uniform_random& rnd)
    {
        size_t size = data_.size();
        for (size_t i = 0; i < size; ++i)
        {
            bool f = rnd.next_Double() < env->cfg_.initial_probability_;
            data_[i] = f;
        }
        changed_ = true;
    }

    TCostType get_Cost() const
    {
        ASSERT_(!changed_);
        return cost_;
    }

    template<class E>
    void evaluate(E* env, uniform_random& rnd)
    {
        if (!changed_)
            return;
        changed_ = false;
        int f = 0;
        size_t size = data_.size();
        for (size_t i = 0; i < size; ++i)
        {
            if (data_[i])
                ++f;
        }
        cost_ = f;
    }

    void assign(OneMax_Solution const& sol)
    {
        ASSERT_(data_.size());
        ASSERT_(data_.size() == sol.data_.size());
        size_t size = data_.size();
        for (size_t i = 0; i < size; ++i)
        {
            data_[i] = sol.data_[i];
        }
        changed_ = sol.changed_;
        cost_    = sol.cost_;
    }

    template<class X>
    void copy_To(X& x) const
    {
        x.assign(*this);
    }

    template<class E>
    void print(std::ostream& os, E* env)
    {
        os << get_Cost() << lf;
    }

    template<class E>
    bool is_Solved(E* env) const
    {
        return cost_ == env->problem_.dimension_;
    }

    template<class E>
    static bool read(E* env, std::string const& file_name, Solution_Pool<OneMax_Solution>& pool)
    {
        std::ifstream in(file_name.c_str(), std::ios::binary);
        if (!in)
        {
            std::cout << "ERROR: can not open '" << file_name << "'" << std::endl;
            return false;
        }
        meta::ifstream_visitor bin_in(in);

        //:read count of solutions
        size_t count;
        bin_in >> count;

        pool.resize(count);
        uniform_random rnd(0);
        for (Solution_Pool<OneMax_Solution>::iterator it = pool.sol_list_.begin();
            it != pool.sol_list_.end(); ++it)
        {
            it->init(env, rnd);
            bin_in >> it->data_;
            it->changed_ = true;
            char magic;
            in.read(&magic, 1);
            if ('*' != magic)
                return false;
        }
        return true;
    }

    template<class E>
    void write(E* env, std::string const& file_name, size_t id, size_t count)
    {
        std::ofstream out(file_name.c_str(), (id ? std::ios::binary | std::ios::app : std::ios::binary));
        meta::ofstream_visitor bin_out(out);
        if (!id)
        {//:write count of solutions
            bin_out << count;
        }
        bin_out << data_;
        out.write("*", 1);
    }

    template<class V>
    void accept(V& v)
    {
        v(cost_);
        v(changed_);
        v(data_);
    }
};
//__________________________________________________________________________________
template<>
struct solution<OneMax_Solution, ga_tag>:
    public OneMax_Solution
{
    typedef solution<OneMax_Solution, ga_tag> OneMax_Solution_GA;
public:
    mutable double wheel_sector_;

    template<class E>
    void cross(E* env, uniform_random& rnd, OneMax_Solution_GA& genome)
    {//:simple one point crossover
        ASSERT_(data_.size());
        ASSERT_(data_.size() == genome.data_.size());
        size_t size = data_.size();
        size_t j = rnd.next_SizeT(size);
        for (size_t i = j; i < size; ++i)
        {
            std::swap(data_[i], genome.data_[i]);
        }
        changed_ = genome.changed_ = true;
    }

    template<class E>
    void mutate(E* env, uniform_random& rnd, double mutationProbability)
    {
        size_t size = data_.size();
        for (size_t i = 0; i < size; ++i)
        {
            if (rnd.next_Double() < mutationProbability)
            {
                data_[i] = !data_[i];
                changed_ = true;
            }
        }
    }

    int get_Fitness() const { return get_Cost(); }
};

//EOF!
